<?php

namespace App\Services;

use App\Constant;
use App\Enums\AuditHistoryType;
use App\Enums\AuthType;
use App\Enums\RegistryStatus;
use App\Events\RegistryAuditPass;
use App\Events\RegistryAuditReject;
use App\Models\Registry;
use App\Models\RegistryAuditHistory;
use App\Models\RegistryAuth;
use App\Models\RegistryFile;
use Carbon\Carbon;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use phpseclib3\Crypt\PublicKeyLoader;
use phpseclib3\Crypt\RSA;

class RegistryService
{
    private $timeout = 3;
    private $cacheSecounds = 60 * 60 * 24; // 24小时
    private $cachePrefix = 'cache_registry_';

    /** 查询作品类别 */
    public function proCat()
    {
        return Cache::remember($this->cachePrefix . 'proCat', $this->cacheSecounds, function () {
            return $this->get('register/dic/proCat');
        });
    }

    /** 查询创作性质 */
    public function proCreateNat()
    {
        return Cache::remember($this->cachePrefix . 'proCreateNat', $this->cacheSecounds, function () {
            return $this->get('register/dic/proCreateNat');
        });
    }

    /** 查询作品发表状态 */
    public function proPubState()
    {
        return Cache::remember($this->cachePrefix . 'proPubState', $this->cacheSecounds, function () {
            return $this->get('register/dic/proPubState');
        });
    }

    /** 查询权利归属方式 */
    public function autBelMethod()
    {
        return Cache::remember($this->cachePrefix . 'autBelMethod', $this->cacheSecounds, function () {
            return $this->get('register/dic/autBelMethod');
        });
    }

    /** 查询申请方式 */
    public function appMethod()
    {
        return Cache::remember($this->cachePrefix . 'appMethod', $this->cacheSecounds, function () {
            return $this->get('register/dic/appMethod');
        });
    }

    /** 查询权利拥有状态 */
    public function autHold()
    {
        return Cache::remember($this->cachePrefix . 'autHold', $this->cacheSecounds, function () {
            return $this->get('register/dic/autHold');
        });
    }

    /** 查询权利取得方式 */
    public function autAcpMethod()
    {
        return Cache::remember($this->cachePrefix . 'autAcpMethod', $this->cacheSecounds, function () {
            return $this->get('register/dic/autAcpMethod');
        });
    }

    /** 创建版权登记  */
    public function createRegistry(Registry $registry)
    {
        if ($registry->register_id) {
            return;
        }
        $registry->load(['productFiles', 'productImages', 'authors', 'owners']);
        $authors = $registry->authors->map(function (RegistryAuth $auth) {
            return $this->authToJson($auth);
        })->toArray();
        $owners = $registry->owners->map(function (RegistryAuth $auth) {
            return $this->authToJson($auth);
        })->toArray();
        $data = [
            'productName' => $registry->product_name,
            'productFile' => implode(',', $registry->productFiles->map(function (RegistryFile $file) {
                return $file->file_path;
            })->toArray()),
            'fileType' => 5,
            'productImage' => implode(',', $registry->productImages->map(function (RegistryFile $file) {
                return $file->file_path;
            })->toArray()),
            'authors' => $authors,
            'copyrights' => $owners,
            'productCreateNatName' => $registry->product_create_nat,
            'productCatName' => $registry->product_cat,
            'duration' => $registry->video_length ?? '',
            'wordCount' => $registry->word_count ?? '',
            'appMethodName' => $registry->apply_method,
            'productCreateTime' => Carbon::parse($registry->product_create_date)->toDateString(),
            'productCreateAddr' => $registry->product_create_address ?: '',
            'productPubStateName' => $registry->is_pub ? '已发表' : '未发表',
            'autAcpMethodName' => $registry->accept_method,
            'autAcpMethodExp' => $registry->accept_method_explain ?: '',
            'autBelMethodName' => $registry->belong_method,
            'autBelMethodExp' => $registry->belong_method_explain,
            'autHoldName' => $registry->hold_names,
            'autHoldExp' => $registry->hold_names_explain,
            'createIntention' => $registry->create_intention,
            'productOriginal' => $registry->originality,
            'enclosure' => implode(',', $registry->attachFiles->map(function (RegistryFile $file) {
                return $file->file_path;
            })->toArray()),
            'signature' => $registry->signature ?: '',

            'channelId' => config('services.registry.client_id'),
            'phoneNum' => $registry->user->mobile,
            'callUrl' => config('services.registry.notify_url'),
        ];
        if ($registry->is_pub) {
            $data['productPubTime'] = Carbon::parse($registry->product_pub_date)->toDateString();
            $data['productPubAddr'] = $registry->product_pub_address;
        }

        try {
            $result = $this->post('register/third/createThirdRegister', $data);
            $registerId = Arr::get($result, 'registerId');
            $registry->status = RegistryStatus::SUBMITTED;
            $registry->register_id = $registerId;
            $registry->save();

            $history = new RegistryAuditHistory();
            $history->registry_id = $registry->id;
            $history->type = AuditHistoryType::SUBMIT;
            $history->title = '提交登记信息';
            $history->save();
        } catch (\Exception $e) {
            $registry->status = RegistryStatus::SUBMIT_FAIL();
            $registry->save();

            $history = new RegistryAuditHistory();
            $history->registry_id = $registry->id;
            $history->type = AuditHistoryType::SUBMIT_FAIL;
            $history->title = '登记信息提交失败';
            $history->remark = $e->getMessage();
            $history->save();
        }
    }

    public function updateRegistry(Registry $registry)
    {
        if ($registry->status->isNot(RegistryStatus::REJECTED())) {
            throw new \Exception('只有审批拒绝的登记申请可以重新提交');
        }
        $registry->load(['productFiles', 'productImages', 'authors', 'owners']);
        $authors = $registry->authors->map(function (RegistryAuth $auth) {
            return $this->authToJson($auth);
        })->toArray();
        $owners = $registry->owners->map(function (RegistryAuth $auth) {
            return $this->authToJson($auth);
        })->toArray();
        $data = [
            'registerId' => $registry->register_id,
            'productName' => $registry->product_name,
            'productImage' => implode(',', $registry->productImages->map(function (RegistryFile $file) {
                return $file->file_path;
            })->toArray()),
            'authors' => $authors,
            'copyrights' => $owners,
            'productCreateNatName' => $registry->product_create_nat,
            'productCatName' => $registry->product_cat,
            'appMethodName' => $registry->apply_method,
            'productCreateTime' => Carbon::parse($registry->product_create_date)->toDateString(),
            'productCreateAddr' => $registry->product_create_address ?: '',
            'productPubStateName' => $registry->is_pub ? '已发表' : '未发表',
            'autAcpMethodName' => $registry->accept_method,
            'autAcpMethodExp' => $registry->accept_method_explain ?: '',
            'autBelMethodName' => $registry->belong_method,
            'autBelMethodExp' => $registry->belong_method_explain,
            'autHoldName' => $registry->hold_names,
            'autHoldExp' => $registry->hold_names_explain,
            'createIntention' => $registry->create_intention,
            'productOriginal' => $registry->originality,
            'enclosure' => implode(',', $registry->attachFiles->map(function (RegistryFile $file) {
                return $file->file_path;
            })->toArray()),
            'signature' => $registry->signature ?: '',

            'channelId' => config('services.registry.client_id'),
            'phoneNum' => $registry->user->mobile,
        ];
        if ($registry->is_pub) {
            $data['productPubTime'] = Carbon::parse($registry->product_pub_date)->toDateString();
            $data['productPubAddr'] = $registry->product_pub_address;
        }

        try {
            $this->post('register/third/upRegisterCom', $data);
            $registry->status = RegistryStatus::SUBMITTED;
            $registry->save();

            $history = new RegistryAuditHistory();
            $history->registry_id = $registry->id;
            $history->type = AuditHistoryType::SUBMIT;
            $history->title = '修改登记信息';
            $history->save();
        } catch (\Exception $e) {
            $registry->status = RegistryStatus::REJECTED;
            $registry->save();

            $history = new RegistryAuditHistory();
            $history->registry_id = $registry->id;
            $history->type = AuditHistoryType::SUBMIT_FAIL();
            $history->title = '登记信息修改失败';
            $history->remark = $e->getMessage();
            $history->save();
        }
    }

    /** 同步登记状态 */
    public function syncRegistry($registryId)
    {
        $registry = Registry::query()->where('register_id', $registryId)->first();
        if (!$registry) {
            return;
        }
        $registryInfo = $this->get('register/register/productRegisterInfo', [
            'registerId' => $registryId,
        ]);
        if ($registry->status->isNot(RegistryStatus::SUBMITTED())) {
            // 只有待审核状态才会进行后续处理
            return;
        }
        $auditStatus = Arr::get($registryInfo, 'auditStatus');
        $auditOpinion = Arr::get($registryInfo, 'auditOpinion');
        if (3 === $auditStatus) {
            // 审核通过
            $certFileUrl = Arr::get($registryInfo, 'registerCertificateUrl');
            $certImageUrls = Arr::get($registryInfo, 'registerCertificateImgUrl');
            $registry->status = RegistryStatus::PASS;
            $registry->cert_file_url = $certFileUrl;
            $registry->cert_image_urls = $certImageUrls;
            $registry->save();

            $history = new RegistryAuditHistory();
            $history->registry_id = $registry->id;
            $history->type = AuditHistoryType::PASS;
            $history->title = '审核通过';
            $history->save();

            RegistryAuditPass::dispatch($registry);
        }
        if (4 === $auditStatus) {
            // 审核未通过
            $registry->status = RegistryStatus::REJECTED;
            $registry->save();

            $history = new RegistryAuditHistory();
            $history->registry_id = $registry->id;
            $history->type = AuditHistoryType::REJECT;
            $history->title = '审核未通过';
            $history->remark = $auditOpinion;
            $history->save();

            RegistryAuditReject::dispatch($registry);
        }
    }

    private function authToJson(RegistryAuth $auth)
    {
        if ($auth->auth_type->is(AuthType::PERSONAL())) {
            return [
                'autType' => 1,
                'realName' => $auth->auth_name,
                'idcardNumber' => $auth->id_card_no,
                'idcardFimg' => $auth->id_card_image_front,
                'idcardBimg' => $auth->id_card_image_back,
                'gender' => $auth->gender,
                'address' => $auth->address,
                'nation' => $auth->nation,
            ];
        }

        return [
            'autType' => 2,
            'companyName' => $auth->auth_name,
            'busLicNum' => $auth->license_no,
            'busLicImg' => $auth->license_image,
            'comAddress' => $auth->address,
            'comValidTime' => $auth->license_expire_date,
        ];
    }

    private function get($path, $query = [])
    {
        $url = $this->getUrl($path);
        if (!empty($query)) {
            $url .= '&' . http_build_query($query);
        }
        \Log::channel(Constant::LOG_CHANNEL_REGISTRY)->info('GET: ' . $url);

        try {
            $response = Http::timeout($this->timeout)->get($url);
        } catch (\Exception $e) {
            \Log::channel(Constant::LOG_CHANNEL_REGISTRY)->info('GET ERROR: ' . $e->getMessage());

            throw $e;
        }

        return $this->parseResult($response);
    }

    private function post($path, $data = [])
    {
        $url = $this->getUrl($path);
        \Log::channel(Constant::LOG_CHANNEL_REGISTRY)->info('POST: ' . $url, $data);
        $publicCert = "-----BEGIN PUBLIC KEY-----\n" .
            wordwrap(config('services.registry.public_key'), 64, "\n", true) .
            "\n-----END PUBLIC KEY-----";
        $key = PublicKeyLoader::loadPublicKey($publicCert);

        $jsonData = json_encode($data);
        $encrypted = '';
        $maxLength = 117;
        while ($jsonData) {
            $input = substr($jsonData, 0, $maxLength);
            $jsonData = substr($jsonData, $maxLength);
            $encrypted .= $key->withPadding(RSA::ENCRYPTION_PKCS1)->encrypt($input);
        }
        $requestData = base64_encode($encrypted);
        \Log::channel(Constant::LOG_CHANNEL_REGISTRY)->info('EncryptData: ' . $requestData);

        try {
            $response = Http::timeout($this->timeout)->post($url, [
                'requestData' => $requestData,
            ]);
        } catch (\Exception $e) {
            \Log::channel(Constant::LOG_CHANNEL_REGISTRY)->info('POST ERROR: ' . $e->getMessage());

            throw $e;
        }

        return $this->parseResult($response);
    }

    private function getUrl($path)
    {
        return sprintf(
            '%s/%s?channelId=%s',
            config('services.registry.host'),
            $path,
            config('services.registry.client_id')
        );
    }

    private function parseResult(Response $resp)
    {
        $json = $resp->body();
        \Log::channel(Constant::LOG_CHANNEL_REGISTRY)->info('Response:' . $json);
        $jsonArr = json_decode($json, true);
        if (!Arr::get($jsonArr, 'success')) {
            throw new \Exception(Arr::get($jsonArr, 'rmsg'));
        }

        return Arr::get($jsonArr, 'result');
    }
}
